#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>

#include "lt_net.h"
#include "lt_unit.h"
#include "lt_logger.h"
#include "lt_string.h"
#include "lt_hashmap.h"
#include "lt_config.h"

// 将 fd 设置为非阻塞
int set_nonblock(int fd)
{
	int opts = fcntl(fd, F_GETFL);
	if ( opts < 0 )
	{
		printf("fd fcntl(f_getfl)");
		return LT_FAILURE;
	}
	opts = opts | O_NONBLOCK;
	if ( fcntl(fd, F_SETFL, opts) < 0 )
	{
		printf("fd fcntl(f_setfl)");
		return LT_FAILURE;
	}
	return LT_SUCCESS;
}

int tcp_client(int argc, char **args)
{
	/*
		参数控制
	*/
	// 是否禁用 Nagle
	int non_nagle = lt_false;

	/*
		IP 与 Port
	*/
	char server_ip[MAX_IP_LENGTH] = "127.0.0.1";
	uint16_t server_port = 12345;

	if ( argc >= 1 )
	{
		strcpy(server_ip, args[1]);
	}
	
	if ( argc >= 2 )
	{
		server_port = atoi(args[2]);
	}

	log_info("Connect to %s : %hd", server_ip, server_port);
	//create tcp socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if ( sockfd < 0 )
    {
		log_error("Create Socket Fd Error");
        printf("CREATE SOCKET ERROR\n");
        return LT_FAILURE;
    }

    //bind是可选的, 绑定 client ip 和 port
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_port = htons(5678);
    client.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(sockfd, (struct sockaddr *)&client, sizeof(client));

    //connect 连接服务器
    struct sockaddr_in ser_addr;
    bzero(&ser_addr, sizeof(ser_addr));
    ser_addr.sin_port = htons(server_port);
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = inet_addr(server_ip);
    if ( connect(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0 )
	{
		printf("Connect TO Server(%s:%hu) failed\n", server_ip, server_port);
		print_sys_error();
        close(sockfd);
		return LT_FAILURE;
	}

	// 禁用 Nagle 算法
	int flag = 1;
    if ( non_nagle && setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0 ) 
	{
		printf("Close TCP Nagle Failed\n");
		print_sys_error();
        close(sockfd);
		return LT_FAILURE;
    }

    char msg[MAX_MSG_LENGTH] = "";
	int msg_len = -1, exit_flat = lt_false;
    // 给服务器发送数据，并接收回复
	while(1)
	{
		bzero(msg, MAX_MSG_LENGTH);
		printf("\nPlease enter the msg to send\n");
		fgets(msg, MAX_MSG_LENGTH, stdin);
		msg[strlen(msg) - 1] = 0;//消除回车的影响
		if ( !strcmp("exit", msg) )
		{
			log_info("Receive exit signal");
			printf("Exiting!!!");
			exit_flat = lt_true;
		}
		msg_len = send(sockfd, msg, strlen(msg), 0);
		if ( msg_len == -1 )
		{
			printf("Send to %s:%hu Failed.", server_ip, server_port);
			log_error("Send to %s:%hu failed", server_ip, server_port);
			print_sys_error();
			continue;
		}
		printf("TCP Client Send %s \n", msg);
		log_info("TCP Client Send %s", msg);

		bzero(msg, MAX_MSG_LENGTH);
		msg_len = recv(sockfd, msg, sizeof(msg), 0);
		if ( msg_len > 0 )
		{
			printf("TCP Server Response %s\n", msg);
			log_info("TCP Server Response %s", msg);
		}
		else
		{
			printf("TCP Server Failed to response\n");
			log_error("TCP Server Failed to response");
			print_sys_error();
		}
		if ( exit_flat )
		{
			break;
		}
	}
    close(sockfd);	
	return LT_SUCCESS;
}

int tcp_server_select(int argc, char **args)
{
	char server_ip[MAX_IP_LENGTH] = "127.0.0.1";
	uint16_t server_port = 12345;

	if ( argc >= 1 )
	{
		strcpy(server_ip, args[1]);
	}
	
	if ( argc >= 2 )
	{
		server_port = atoi(args[2]);
	}

	int clients_fd[FD_SETSIZE - 2];
	int max_fd = -1, clients_count = -1;

	fd_set set_tmp, poll_set;

	for ( int i = 0; i < FD_SETSIZE - 2; i++ )
	{
		clients_fd[i] = -1;
	}

    //1、创建TCP监听套接字
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if ( listen_fd < 0 )
    {
		log_error("Create Socket fd Failed");
		printf("Create Server FD Failed\n");
		return LT_FAILURE;
    }

    //端口复用
    int yes = 1;
    setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &yes, sizeof(yes));

    //2、给socket绑定ip和端口信息
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip);
    int result = bind(listen_fd, (struct sockaddr *)&server, sizeof(server));
    if (result == -1)
    {
		log_error("Failed to bind Server Net Address");
		printf("Failed to bind Server Net Address");
		return LT_FAILURE;
    }

    // 3、调用listen
    listen(listen_fd, 10);

	FD_ZERO(&poll_set);
	FD_SET(listen_fd, &poll_set);
	max_fd = listen_fd;

	printf("TCP Server Listen On %s:%hu with fd %d\n", server_ip, server_port, listen_fd);

	// 声明一个 hashmap 变量，用于保存 句柄与主机网络地址的映射关系。
	HashMap *fd_m_addr = xmap_init(64);
    //4、提取建立完成的链接
    //accept调用一次只能接待一个客户端,需要反复调用accept
    while(1)
    {
		// tmp 变量只在本次循环有效，所以需要使用 poll_set 保存变量，每次循环开始重新赋值。
		set_tmp = poll_set;
		int ready_count = select(max_fd + 1, &set_tmp, NULL, NULL, NULL);
		if ( ready_count < 0 )
		{
			printf("Failed to execute select\n");
			log_error("Failed to execute select");
			break;
		}
		else if ( ready_count > 0 )
		{
			char hm_key[5] = "", hm_value[MAX_HOST_LENGTH] = "";
			// 先检查监听是否准备完毕
			if ( FD_ISSET(listen_fd, &set_tmp) )
			{
				struct sockaddr_in client;
				socklen_t len = sizeof(client);
				int client_fd = accept(listen_fd, (struct sockaddr *)&client, &len);

				for ( int i = 0; i < FD_SETSIZE - 2; i++ )
				{
					if ( clients_fd[i] == -1 )
					{
						clients_fd[i] = client_fd;
						if ( clients_count < i + 1 )
						{
							clients_count = i + 1;
						}
						break;
					}
				}
				log_info("FD SetSize %d", FD_SETSIZE);
				if ( clients_count < FD_SETSIZE - 1 )
				{
					FD_SET(client_fd, &poll_set);
					max_fd = client_fd > max_fd? client_fd: max_fd;
					//遍历客户端信息
					char ip[MAX_IP_LENGTH] = "";
					unsigned short port = ntohs(client.sin_port);
					inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, MAX_IP_LENGTH);
					printf("client %s is connected %hu port\n", ip, port);
					log_info("client %s is connected %hu port", ip, port);

					sprintf(hm_key, "%d", client_fd);
					sprintf(hm_value, "%s:%hu", ip, port);
					xmap_put(fd_m_addr, hm_key, hm_value, MAX_HOST_LENGTH);
				}
				else
				{
					printf("Number of Clients reaches max limit\n");
				}
				ready_count--;
			}

			for ( int i = 0; i < clients_count && ready_count > 0; i++ )
			{
				int client_fd = clients_fd[i];
				if ( client_fd < 0 )
				{
					continue;
				}
				if ( FD_ISSET(client_fd, &set_tmp) )
				{
					char msg[MAX_MSG_LENGTH] = "";
					char msg_res[MAX_MSG_LENGTH] = "Recevied Successfully";
					int len = recv(client_fd, msg, sizeof(msg), 0);
					sprintf(hm_key, "%d", client_fd);
					char *addr = xmap_get(fd_m_addr, hm_key);
					if ( len <= 0 )
					{
						close(client_fd);
						clients_fd[i] = -1;
						FD_CLR(client_fd, &poll_set);
						printf("Release host %s\n", addr);
						xmap_del(fd_m_addr, hm_key);
					}
					else
					{
						printf("TCP Client(%s) Send: %s\n", addr, msg);
						if ( send(client_fd, msg_res, strlen(msg_res), 0) > 0 )
						{
							printf("---- Response Successfully With %s\n\n", msg_res);
						}

						to_lower_case(msg);
						//printf("--%s--\n", msg);
						if ( !strcmp("exit", msg) )	
						{
							close(client_fd);
							clients_fd[i] = -1;
							FD_CLR(client_fd, &poll_set);
							printf("Release Host %s\n", addr);
							xmap_del(fd_m_addr, hm_key);
						}
					}
					--ready_count;
				}
			}
		}
		// select interval ： 10ms
		usleep(10000);
    }
    close(listen_fd);
	// 释放 hashmap 变量占用的内存
	xmap_fint(fd_m_addr);
	return 0;
}

int tcp_server_epoll(int argc, char **args)
{
	char server_ip[MAX_IP_LENGTH] = "127.0.0.1";
	uint16_t server_port = 12345;

	if ( argc >= 1 )
	{
		strcpy(server_ip, args[1]);
	}
	
	if ( argc >= 2 )
	{
		server_port = atoi(args[2]);
	}

	int epoll_fd = -1;
	struct epoll_event ev, events[MAX_EPOLL_RD_LIST];

    // 创建 TCP Server 监听套接字
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if ( listen_fd < 0 )
    {
		log_error("Create Socket fd Failed");
		printf("Create Server FD Failed\n");
		return LT_FAILURE;
    }

    // 服务端端口复用
    int yes = 1;
    setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &yes, sizeof(yes));

    // 给服务端 socket 绑定 ip 和端口信息
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip);
    int result = bind(listen_fd, (struct sockaddr *)&server, sizeof(server));
    if (result == -1)
    {
		log_error("Failed to bind Server Net Address");
		printf("Failed to bind Server Net Address");
		close(listen_fd);
		return LT_FAILURE;
    }

    // 服务端调用 listen 函数监听客户端三次握手队列
    listen(listen_fd, 10);

	printf("TCP Server(epoll) Listen On %s:%hu with fd %d\n", server_ip, server_port, listen_fd);

	epoll_fd = epoll_create1(0);
	if ( epoll_fd < 0 )
	{
		printf("epoll fd is created failed\n");
		close(listen_fd);
		close(epoll_fd);
		return LT_FAILURE;
	}
	ev.events = EPOLLIN;
	ev.data.fd = listen_fd;
	if ( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) < 0 )
	{
		printf("failed to execute epoll_ctl(EPOLL_CTL_ADD)\n");
		close(listen_fd);
		close(epoll_fd);
		return LT_FAILURE;
	}

	// 声明一个 hashmap 变量，用于保存 句柄与主机网络地址的映射关系。
	HashMap *fd_m_addr = xmap_init(64);
	int done = 0;
    while( !done )
    {
		int ready_count = epoll_wait(epoll_fd, events, MAX_EPOLL_RD_LIST, 1000);
		if ( ready_count < 0 )
		{
			printf("Failed to execute epoll\n");
			log_error("Failed to execute epoll");
			break;
		}
		//printf("ready count %d\n", ready_count);
		for ( int i = 0; i < ready_count; i++ )
		{
			char hm_key[5] = "", hm_value[MAX_HOST_LENGTH] = "";
			// 先检查监听是否准备完毕
			if ( events[i].data.fd == listen_fd )
			{
				struct sockaddr_in client;
				socklen_t len = sizeof(client);

				int client_fd = accept(listen_fd, (struct sockaddr *)&client, &len );
				if ( client_fd > 0 )
				{
					set_nonblock(client_fd);
					struct epoll_event ev_client;
					ev_client.data.fd = client_fd;
					ev_client.events = EPOLLIN;
					// ev_client.events = EPOLLIN | EPOLLOUT;
					if ( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev_client) < 0 )
					{
						printf("failed to execute epoll_ctl(EPOLL_CTL_ADD)");
						close(client_fd);
						continue;
					}
					// 获取客户端信息
					char ip[MAX_IP_LENGTH] = "";
					unsigned short port = ntohs(client.sin_port);
					inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, MAX_IP_LENGTH);
					printf("client %s:%hu is connected with fd %d\n", ip, port, client_fd);
					log_info("client %s:%hu is connected", ip, port);

					sprintf(hm_key, "%d", client_fd);
					sprintf(hm_value, "%s:%hu", ip, port);
					xmap_put(fd_m_addr, hm_key, hm_value, MAX_HOST_LENGTH);
				}
				else if ( errno != EAGAIN && errno != EWOULDBLOCK ) 
				{
					printf("execute accept failed\n");
					done = 1;
				}
			}
			else
			{
				int client_fd = events[i].data.fd;
				if ( events[i].events & EPOLLIN )
				{
					// printf("TCP Client Fd is ready reading\n");
					char msg[MAX_MSG_LENGTH] = "";
					char msg_res[MAX_MSG_LENGTH] = "Recevied Successfully";
					int len = recv(client_fd, msg, sizeof(msg), 0);
					sprintf(hm_key, "%d", client_fd);
					char *addr = xmap_get(fd_m_addr, hm_key);
					if ( len <= 0 )
					{
						close(client_fd);
						epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);
						printf("Release host %s\n", addr);
						xmap_del(fd_m_addr, hm_key);
					}
					else
					{
						printf("TCP Client(%s) Send: %s\n", addr, msg);
						if ( send(client_fd, msg_res, strlen(msg_res), 0) > 0 )
						{
							printf("---- Response Successfully With %s\n\n", msg_res);
						}

						to_lower_case(msg);
						//printf("--%s--\n", msg);
						if ( !strcmp("exit", msg) )	
						{
							close(client_fd);
							epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);
							printf("Release Host %s\n", addr);
							xmap_del(fd_m_addr, hm_key);
						}
					}
				}
				//if ( events[i].events & EPOLLOUT )
				//{
				//	printf("%d is ready write\n", client_fd);
				//}
				//else if ( (events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP ) )
				//{
				//	printf("%d is error\n", client_fd);
				//	print_sys_error();
				//}
			}
		}
    }
    close(listen_fd);
	close(epoll_fd);
	// 释放 hashmap 变量占用的内存
	xmap_fint(fd_m_addr);
	return 0;
}

int udp_client(int argc, char **args)
{

	return 0;
}

int udp_server(int argc, char **args)
{

	return 0;
}